<template>
    <div id="container"></div>
</template>

<script>
import * as THREE from "./three.module.js";
import { OrbitControls } from "three/examples/jsm/controls/OrbitControls.js";
import { TWEEN } from "three/examples/jsm/libs/tween.module.min.js";
var globeTextureLoader = new THREE.TextureLoader();

export default {
    name:"Earth",
    data(){
        return{
            Dom:null,
            width:window.innerWidth,
            height:window.innerHeight,
            radius: 5,
            group: new THREE.Group(),
            groupDots: new THREE.Group(),
            groupLines: new THREE.Group(),
            groupHalo: new THREE.Group(),
            aGroup: new THREE.Group(),
            renderer:null, 
            camera:null,
            scene:null, 
            controls:null, 
            stars:null,
            clock: new THREE.Clock(),
            posArr:[
                {
                    x: -2.010413250431975,
                    y: 2.5834138389882897,
                    z: -3.7794459248693224,
                },
                {
                    x: -1.701912480642894,
                    y: 3.184429573676825,
                    z: -3.4587428638920796,
                },
                {
                    x: -2.1887440518384498,
                    y: 2.534683543471576,
                    z: -3.7127858556609734,
                },
                {
                    x: -1.7760403450231674,
                    y: 1.8940507443801071,
                    z: -4.272967642115155,
                },
                {
                    x: -1.2387548768209078,
                    y: 2.467432662080366,
                    z: -4.1686043723591,
                },

                {
                    x: -0.9326350073394328,
                    y: 2.8399222968004114,
                    z: -4.00812091773949,
                },
                {
                    x: 3.469198597393574,
                    y: 1.2295167303380952,
                    z: -3.3842206934036057,
                },
                {
                    x: -2.0636200279017873,
                    y: 0.8444294629976027,
                    z: -4.493027615657812,
                },
                {
                    x: 0.47725894517680106,
                    y: 2.4327372143508037,
                    z: -4.34212085796347,
                },
                {
                    x: -0.03915748918627658,
                    y: -0.008362945319338826,
                    z: 4.999839672648135,
                },
                {
                    x: 0.8544382232162094,
                    y: 1.5274953155132989,
                    z: 4.683662390031124,
                },
                {
                    x: 3.0409624989238546,
                    y: 1.76433738825175,
                    z: -3.555230043268055,
                },
                {
                    x: 2.1518961827021106,
                    y: 3.891904027152385,
                    z: -2.285262755638206,
                },
            ],
            earth:require('@/assets/earth/earth2.jpg'),
            ball:require('@/assets/earth/ball.png'),
            halo:require('@/assets/earth/halo.png'),
            smallEarth:require('@/assets/earth/smallEarth.png'),
            animationId:null
        }
    },
    mounted(){
        this.Dom = document.querySelector("#container");
        this.initUniverse();
        window.addEventListener("resize", this.onWindowResize, true);
    },
    beforeDestroy(){
        window.removeEventListener("resize", this.onWindowResize, true)
        this.clearScene();
    },
    methods:{
        clearScene() {
            cancelAnimationFrame(this.animationId);
            this.scene.traverse((child) => {
                if (child.material) {
                    child.material.dispose();
                }
                if (child.geometry) {
                    child.geometry.dispose();
                }
                child = null;
            });
            this.renderer.forceContextLoss();
            this.renderer.dispose();
            // this.scene.clear();
            this.flows = [];
            this.scene = null;
            this.camera = null;
            this.controls = null;
            this.renderer.domElement = null;
            this.renderer = null;
            this.sceneDomElement = null;
            console.log('clearScene');
        },
        // 执行整个动画
        initUniverse(){
            this.initScene();
            this.initCamera();
            this.initRenderer();
            this.initLight();
            // this.initControls();
            this.createStarSky();
            this.initEarth();
            this.createHalo();
            this.animate();
            const option = {
                position: [5, -5, 20], //新的位置
                controls: [5, 1, 0],
                duration: 3000,
                start: function () {
                    console.log("开始了");
                },
                update: function () {
                    console.log("飞行中");
                },
                done: function () {
                    console.log("结束了");
                },
                stop: function () {
                    console.log("停止了");
                },
            };
            // this.flyTo(TWEEN, this.controls, option);
            
        },
        //初始化渲染场景
        initRenderer(){
            this.renderer = new THREE.WebGLRenderer({
                antialias: true,
                alpha: true,
            });
            this.renderer.setPixelRatio(window.devicePixelRatio);
            this.renderer.setSize(this.width, this.height);
            this.Dom.appendChild(this.renderer.domElement);
            
        },
        //初始化相机
        initCamera() {
            this.camera = new THREE.PerspectiveCamera(
                45,
                this.width / this.height,
                1,
                10000
            );
            this.camera.position.set(12, 4, 15);
            // this.camera.position.set(12, 4, 15);
            this.camera.lookAt(10, 3, 10);
        },
        //初始化场景
        initScene(){
            this.scene = new THREE.Scene();
            this.scene.background = new THREE.Color(0x020924);
            this.scene.fog = new THREE.Fog(0x020924, 200, 1000);
        },
        /**
         * 初始化用户交互
         **/
        initControls(){
            this.controls = new OrbitControls(this.camera, this.renderer.domElement);
            this.controls.enableDamping = false;
            this.controls.enableZoom = false;
            this.controls.autoRotate = false;
            this.controls.autoRotateSpeed = 2;
            this.controls.enablePan = false;
        },
        /**
         * @description 初始化光
         */
        initLight(){
            const ambientLight = new THREE.AmbientLight(0xcccccc, 1.1);
            this.scene.add(ambientLight);
            var directionalLight = new THREE.DirectionalLight(
                0xffffff,
                0.2
            );
            directionalLight.position.set(1, 0.1, 0).normalize();
            var directionalLight2 = new THREE.DirectionalLight(
                0xff2ffff,
                0.2
            );
            directionalLight2.position.set(1, 0.1, 0.1).normalize();
            this.scene.add(directionalLight);
            this.scene.add(directionalLight2);
            var hemiLight = new THREE.HemisphereLight(
                0xffffff,
                0x444444,
                0.2
            );
            hemiLight.position.set(0, 1, 0);
            this.scene.add(hemiLight);
            var directionalLight = new THREE.DirectionalLight(0xffffff);
            directionalLight.position.set(1, 500, -20);
            directionalLight.castShadow = true;
            directionalLight.shadow.camera.top = 18;
            directionalLight.shadow.camera.bottom = -10;
            directionalLight.shadow.camera.left = -52;
            directionalLight.shadow.camera.right = 12;
            this.scene.add(directionalLight);
        },
        /**
         * @description 渲染
         */
        renders(){
            this.renderer.clear();
            this.renderer.render(this.scene, this.camera);
        },
        /**
         * 窗口变动
         **/
        onWindowResize(){
            this.width = window.innerWidth;
            this.height = window.innerHeight;
            this.camera.aspect = this.width / this.height;
            this.camera.updateProjectionMatrix();
            this.renderer.setSize(this.width, this.height);
            this.renders();
            this.renderer.setSize(this.width, this.height);
        },
        /**
         * 补间飞行
         */
        flyTo(TWEEN, controls, option){
            if (!TWEEN) throw "请传入 TWEEN";
            option.position = option.position || []; //相机新的位置
            option.controls = option.controls || []; //控制器新的中心点位置(围绕词典旋转等)
            option.duration = option.duration || 1000; //飞行时间
            option.easing = option.easing || TWEEN.Easing.Linear.None;
            TWEEN.removeAll();
            const curPosition = this.camera.position,
                controlsTar = this.controls.target,
                tween = new TWEEN.Tween({
                    x1: curPosition.x, // 相机当前位置x
                    y1: curPosition.y, // 相机当前位置y
                    z1: curPosition.z, // 相机当前位置z
                    x2: controlsTar.x, // 控制当前的中心点x
                    y2: controlsTar.y, // 控制当前的中心点y
                    z2: controlsTar.z, // 控制当前的中心点z
                })
                    .to(
                        {
                            x1: option.position[0], // 新的相机位置x
                            y1: option.position[1], // 新的相机位置y
                            z1: option.position[2], // 新的相机位置z
                            x2: option.controls[0], // 新的控制中心点位置x
                            y2: option.controls[1], // 新的控制中心点位置x
                            z2: option.controls[2], // 新的控制中心点位置x
                        },
                        option.duration
                    )
                    .easing(TWEEN.Easing.Linear.None); // TWEEN.Easing.Cubic.InOut //匀速
            tween.onUpdate(() => {
                this.controls.enabled = false;
                this.camera.position.set(
                    tween._object.x1,
                    tween._object.y1,
                    tween._object.z1
                );
                this.controls.target.set(
                    tween._object.x2,
                    tween._object.y2,
                    tween._object.z2
                );
                this.controls.update();
                if (option.update instanceof Function) option.update(tween);
            });
            tween.onStart(() => {
                this.controls.enabled = false;
                if (option.start instanceof Function) option.start();
            });
            tween.onComplete(() => {
                this.controls.enabled = true;
                if (option.done instanceof Function) option.done();
            });
            tween.onStop(() =>
                option.stop instanceof Function ? option.stop() : ""
            );
            tween.start();
            TWEEN.add(tween);
            return tween;
        },
        /**
         * 更新
         **/
        animate(){
            this.animationId = requestAnimationFrame(() => {
                if (this.controls) this.controls.update(this.clock.getDelta());
                this.groupHalo.rotation.z = this.groupHalo.rotation.z + 0.01;
                // this.group.rotation.y += 0.001;
                if (this.stars) {
                    this.stars.rotation.y += 0.0001;
                }
                this.renders();
                this.animate();
                TWEEN.update();
            });
        },
        /**生成星空 */
        createStarSky(){
            const positions = [];
            const colors = [];
            const geometry = new THREE.BufferGeometry();
            for (var i = 0; i < 10000; i++) {
                var vertex = new THREE.Vector3();
                vertex.x = Math.random() * 2 - 1;
                vertex.y = Math.random() * 2 - 1;
                vertex.z = Math.random() * 2 - 1;
                positions.push(vertex.x, vertex.y, vertex.z);
                var color = new THREE.Color();
                color.setHSL(
                    Math.random() * 0.2 + 0.5,
                    0.55,
                    Math.random() * 0.25 + 0.55
                );
                colors.push(color.r, color.g, color.b);
            }
            geometry.setAttribute(
                "position",
                new THREE.Float32BufferAttribute(positions, 3)
            );
            geometry.setAttribute(
                "color",
                new THREE.Float32BufferAttribute(colors, 3)
            );

            globeTextureLoader.load(this.ball,texture=>{
                var starsMaterial = new THREE.PointsMaterial({
                    map: texture,
                    size: 1,
                    transparent: true,
                    opacity: 1,
                    vertexColors: true, //true：且该几何体的colors属性有值，则该粒子会舍弃第一个属性--color，而应用该几何体的colors属性的颜色
                    blending: THREE.AdditiveBlending,
                    sizeAttenuation: true,
                    // verticesNeedUpdate: true,
                });
                this.stars = new THREE.Points(
                    geometry,
                    starsMaterial
                );
                this.stars.scale.set(300, 300, 300);
                this.scene.add(this.stars);
            });
        },
        // 初始化地球
        initEarth(){
            globeTextureLoader.load(this.earth,texture=>{
                var globeGgeometry = new THREE.SphereGeometry(
                    this.radius,
                    100,
                    100
                );
                var globeMaterial = new THREE.MeshStandardMaterial({
                    map: texture,
                });
                var globeMesh = new THREE.Mesh(
                    globeGgeometry,
                    globeMaterial
                );
                this.group.rotation.set(0.3, 3.5, 0.1);
                this.group.add(globeMesh);
                this.scene.add(this.group);
            })
        },
        // 计算v1,v2 的中点
        getVCenter(v1, v2){
            let v = v1.add(v2);
            return v.divideScalar(2);
        },
        // 计算V1，V2向量固定长度的点
        getLenVcetor(v1, v2, len){
            let v1v2Len = v1.distanceTo(v2);
            return v1.lerp(v2, len / v1v2Len);
        },
        /**
         * 添加线条
         */
        addLines(v0, v3){
            // 夹角
            var angle = (v0.angleTo(v3) * 1.8) / Math.PI / 0.1; // 0 ~ Math.PI
            var aLen = angle * 0.4,
                hLen = angle * angle * 12;
            var p0 = new THREE.Vector3(0, 0, 0);

            // 开始，结束点
            // var v0 = groupDots.children[0].position;
            // var v3 = groupDots.children[1].position;

            // 法线向量
            var rayLine = new THREE.Ray(
                p0,
                this.getVCenter(v0.clone(), v3.clone())
            );

            // 顶点坐标
            var vtop = rayLine.at(hLen / rayLine.at(1).distanceTo(p0));

            // 控制点坐标
            var v1 = this.getLenVcetor(v0.clone(), vtop, aLen);
            var v2 = this.getLenVcetor(v3.clone(), vtop, aLen);

            // 绘制贝塞尔曲线
            var curve = new THREE.CubicBezierCurve3(v0, v1, v2, v3);
            var geometry = new THREE.Geometry();
            geometry.vertices = curve.getPoints(50);

            var material = new THREE.LineBasicMaterial({
                // color: '#FF0000',
                // vertexColors: true
                linewidth: 40,
                // linewidth: 0.0004,
                vertexColors: true,
                // dashed: false,
            });
            geometry.colors = curve
                .getPoints(50)
                .map((item, index) =>
                    index > 25
                        ? new THREE.Color(0xe2b591)
                        : new THREE.Color(0x458cda)
                );

            // Create the final object to add to the scene
            return {
                curve: curve,
                lineMesh: new THREE.Line(geometry, material),
            };
        },
        /**
         * @desc 随机设置点
         * @param <Group> group ...
         * @param <number> radius ...
         */
        setRandomDot(group, radius){
            this.posArr.map((pos) => {
                let dotGeo = new THREE.SphereGeometry(
                    radius,
                    radius,
                    radius
                );
                let dotMater = new THREE.MeshPhongMaterial({
                    color: "tomato",
                });
                let dotMesh = new THREE.Mesh(dotGeo, dotMater);
                dotMesh.position.set(pos.x, pos.y, pos.z);
                group.add(dotMesh);
            });
        },
        // 光环
        createHalo(){
            globeTextureLoader.load(this.halo,texture=>{
                var geometry = new THREE.PlaneGeometry(14, 14); //矩形平面
                var material = new THREE.MeshLambertMaterial({
                    map: texture, //给纹理属性map赋值
                    transparent: true,
                    side: THREE.DoubleSide, //两面可见
                }); //材质对象
                var mesh = new THREE.Mesh(geometry, material); //网格模型对象
                this.groupHalo.add(mesh);
            })

            // 小地球
            globeTextureLoader.load(this.smallEarth,texture=>{
                var geometry = new THREE.Geometry(); //声明一个空几何体对象
                var p1 = new THREE.Vector3(-7, 0, 0); //顶点1坐标
                var p2 = new THREE.Vector3(7, 0, 0); //顶点2坐标
                geometry.vertices.push( p2); //顶点坐标添加到geometry对象
                var material = new THREE.PointsMaterial({
                    map: texture, //给纹理属性map赋值
                    transparent: true,
                    side: THREE.DoubleSide, //两面可见
                    size: 1, //点对象像素尺寸
                }); //材质对象
                var points = new THREE.Points(geometry, material); //点模型对象
                this.groupHalo.add(points); //点对象添加到场景中
            })

            this.groupHalo.rotation.set(1.9, 0.5, 1);
            this.scene.add(this.groupHalo);

            // 小点
            this.setRandomDot(this.groupDots, 0.02);

            this.group.add(this.groupDots);

            // 曲线
            var animateDots = [];
            this.groupDots.children.forEach((elem) => {
                var line = this.addLines(
                    this.groupDots.children[0].position,
                    elem.position
                );
                this.groupLines.add(line.lineMesh);
                animateDots.push(line.curve.getPoints(100));
            });
            this.group.add(this.groupLines);

            // 添加动画
            for (let i = 0; i < animateDots.length; i++) {
                let aGeo = new THREE.SphereGeometry(0.03, 1, 0.03);
                let aMater = new THREE.MeshPhongMaterial({ color: "#fff" });
                let aMesh = new THREE.Mesh(aGeo, aMater);
                this.aGroup.add(aMesh);
            }

            var vIndex = 0;
            let that = this;
            function animateLine() {
                that.aGroup.children.forEach((elem, index) => {
                    let v = animateDots[index][vIndex];
                    elem.position.set(v.x, v.y, v.z);
                });
                vIndex++;
                if (vIndex > 100) {
                    vIndex = 0;
                }
                setTimeout(animateLine, 20);
            }
            this.group.add(this.aGroup);
            animateLine();
        },
    }
}
</script>

<style lang="scss" scoped>
#container{
    width: 70vw;
    height: 100vh;
}
</style>